plinstrument:
{
calc:"
// (based on HANDS LITE LED 500 BSW)

#include	colorwheel
#include	dim4
#include	gobo
#include	ledref
#include	strobe


// controls
cSht; cDim; cC; cM; cY; cCol; cClh; cGob; cPsm; cPsR; cPsm2; cFrs; cFcs; cPan; cTlt; cSpd; cFnc;

// parameters
pRPn; pRTl; pClf; pGob;

// constants
kPosY = 0.0;
kDia = 0.186;
kBaseWid = 0.356;
kBaseHi  = 0.12;
kBaseDep = 0.28;
kArmWid = 0.374;
kArmDep = 0.121;
kCenterHi = 0.44;
kZBack = 0.225;
kZFront = -0.24;
kHeadWid = 0.245;
kHeadHi = 0.245;
kImageRad = 0.019 * 0.5;

kCnt = 1;	// (?)
kSpread = Spread(kCnt, 0.5, 1.8);	// (beam angle?)
kCtp = 7500;

kSource = VecScale(CTemp(kCtp),
			PowerHalogen(420, kDia));
		//	LEDRefConv(400, kDia, kLEDRefWatt_W, kLEDRefDia_W, kLEDRefSrc_W));


// protocol parameters
CENTER[1] = kCenterHi;
PAN;
TILT;
LIGHT;
SHAPE;
TEX;
CTLS;
PROGRAM;
UNIVAR;

PRM=1;
CTL;


// pan, tilt

kPan = 540;
kTilt = 270;
kPanSpeedMin = 530 / 200;		// [deg/s] (of Power Spot 250)
kPanSpeedMax = 530 / 2.65;		// [deg/s] (of Power Spot 250)
kTiltSpeedMin = 285 / 110;		// [deg/s] (of Power Spot 250)
kTiltSpeedMax = 285 / 1.8;		// [deg/s] (of Power Spot 250)

gPan;
gTilt;

PanVal#
(
	(pRPn? -1 : 1) * (cPan / 256 - 0.5)
);

TiltVal#
(
	(pRTl? -1 : 1) * (cTlt / 256 - 0.5)
);


PanTiltTime#
(
	pd = kPan  * PanVal() - gPan;
	td = kTilt * TiltVal() - gTilt;

	(pd != 0 | td != 0)?	// change
	(
		ps = ParamSqr(kPanSpeedMax, kPanSpeedMin, 0, 255, cSpd);
		ts = ParamSqr(kTiltSpeedMax, kTiltSpeedMin, 0, 255, cSpd);
		gPan  += LimitChange($0, pd, ps);
		gTilt += LimitChange($0, td, ts);
		1
	)
	:
	0
);


// shutter

kStrobeMin = 1;			// [Hz] (?)
kStrobeMax = 10;		// [Hz] (?)
kStrobeDur = 1 / 15;	// [s] (?)

gShutter;
gStrobe[0];

ShutterTime#
(
	(cSht < 4)?	// close
	(
		gShutter = 0;
	)
	:(cSht < 104)?	// strobe
	(
		ss = ParamSqr(kStrobeMin, kStrobeMax, 4, 103, cSht);
		StrobeProgress(gStrobe, $0 * ss, kStrobeDur * ss);
		gShutter = gStrobe.ef;
	)
	:(cSht < 108)?	// open
	(
		gShutter = 1;
	)
	:(cSht < 158)?	// closing
	(
		ss = ParamSqr(kStrobeMin, kStrobeMax, 108, 157, cSht);
		gShutter = StrobeClosing(gStrobe, $0 * ss, kStrobeDur * ss);
	)
	:(cSht < 208)?	// opening
	(
		ss = ParamSqr(kStrobeMin, kStrobeMax, 207, 158, cSht);
		gShutter = StrobeOpening(gStrobe, $0 * ss, kStrobeDur * ss);
	)
	:(cSht < 213)?	// open
	(
		gShutter = 1;
	)
	:(cSht < 252)?	// random strobe
	(
		ss = StrobeRandomSpeed(gStrobe, kStrobeMin, kStrobeMax, 0.5, ParamLin(0.25, 0.75, 213, 251, cSht));
		StrobeProgress(gStrobe, $0 * ss, kStrobeDur * ss);
		gShutter = gStrobe.ef;
	)
	:		// open
	(
		gShutter = 1;
	);
);


// dimmer

gDimmer;

DimmerTime#
(
	gDimmer = cDim / 256;
);


// cmy

kCMYSpeedMax = 2.9;		// [1/s] (of Alpha Spot HPE 300)

gCMY;	// (rgb)

CMYTime#
(
	gCMY[0] += LimitChange($0, ParamLin(1, 0, 0, 255, cC) - gCMY[0], kCMYSpeedMax);
	gCMY[1] += LimitChange($0, ParamLin(1, 0, 0, 255, cM) - gCMY[1], kCMYSpeedMax);
	gCMY[2] += LimitChange($0, ParamLin(1, 0, 0, 255, cY) - gCMY[2], kCMYSpeedMax);
);


// color
kColors = 15;
kColorSpeedMax = 2.9;		// [1/s] (of XL 700)
kColorSpeedMin = 0.01;		// [1/s] (of XL 700)

gColor[0];

ColorTime#
(
	(cCol < 128)?	// continuous position
	(
/*		pLnc?	// linear
		(
			cd = cCol / 128;
		)
*/
		(cClh < 64)?	// full color
		(
			cd = floor( ParamLin(0, kColors, 0, 128, cCol) ) / kColors;
		)
		:		// half color
		(
			cd = floor( ParamLin(0, 2 * kColors, 0, 128, cCol) ) / (2 * kColors);
		);
	)
	:
	(
		cd = -1;
		
		(cCol < 191)?	// bwd(?)
		(
			cs = -ParamSqr(kColorSpeedMin, kColorSpeedMax, 190, 128, cCol);
		)
		:(cCol < 193)?	// stop
		(
		)
		:				// fwd(?)
		(
			cs =  ParamSqr(kColorSpeedMin, kColorSpeedMax, 193, 255, cCol);
		);
	);

	(0 <= cd)?	// move to
	(
		GoboPos(gColor, $0, cd, kColorSpeedMax);
	)
	:			// rotate
	(
		GoboRotate(gColor, $0, cs);
	);
	
	(gColor.whl != gColor.pos)?
	(
		gColor.whl = gColor.pos;
		TEX = 1;
	);
);


// gobo

kGobos = 16;
kGoboSpeedMax = 1.0;			// [1/s] (of XL 700)
kGoboSpeedMin = 0.01;			// [1/s] (of XL 700)
kGoboRotSpeedMax = 3.8;			// [1/s] (of XL 700)
kGoboRotSpeedMin = 2.4 / 3600;	// [1/s] (of XL 700)
kGoboShake = 0.05 / kGobos;		// [cycle] (of XL 700)
kGoboShakeSpeedMin = 0.1;	// [1/s] (of XL 700)
kGoboShakeSpeedMax = 2;		// [1/s] (of XL 700)

gGobo[0];

GoboTime#
(
	(cGob < 16)?	// open
	(
	)
	:(cGob < 48)?	// sel
	(
		gd = floor( ParamLin(1, kGobos, 16, 48, cGob) ) / kGobos;
	)
	:(cGob < 128)?
	(
		gd = floor((cGob - 48) / 5) / kGobos;
		sk = ParamSqr(kGoboShakeSpeedMin, kGoboShakeSpeedMax, 0, 4, Mod(cGob - 48, 5));
	)
	:
	(
		gd = -1;
		(cGob < 188)?	// fwd
		(
			gs =  ParamSqr(kGoboSpeedMin, kGoboSpeedMax, 187, 120, cGob);
		)
		:(cGob < 196)?	// stop
		(
		)
		:				// rev
		(
			gs = -ParamSqr(kGoboSpeedMin, kGoboSpeedMax, 196, 255, cGob);
		);
	);

	(0 <= gd)?	// move to
	(
		GoboPos(gGobo, $0, gd, kGoboSpeedMax);
		gd = gGobo.pos;
		sk?
		(
			gd += kGoboShake * GoboShake(gGobo, $0, sk);
		);
	)
	:			// rotate
	(
		GoboRotate(gGobo, $0, gs);
		gd = gGobo.pos;
	);

	(gGobo.whl != gd)?
	(
		gGobo.whl = gd;
		TEX = 1;
	);
);


// prism
kPrismDelta = 0.2;	// (?)
kPrismSize = PrismSize(kPrismDelta);
kPrism2Delta = 0.3;	// (?)
kPrism2Size = PrismSize(kPrism2Delta);

kPrismSpeedMax = 4.4;	// [1/s] (of XL 700)
kPrismSpeedMin = 0.01;	// [1/s] (of XL 700)

/*
prism obj
.use : prism use
*/


PrismTimed#		// (time, obj, use, rot)
(
	po = $1;
	pc = $2;
	rc = $3;

	(pc < 64)?		// none
	(
	)
	:	// use
	(
		pu = 1;

		(rc < 128)?		// index
		(
			pd = Cycle( ParamLin(0, 400 / 360, 0, 128, rc) );
		)
		:					// rotate
		(
			pd = -1;

			(rc < 188)?		// rev
			(
				ps = -ParamSqr(kPrismSpeedMin, kPrismSpeedMax, 187, 128, rc);
			)
			:(rc < 196)?	// stop
			(
			)
			:		// fwd
			(
				ps =  ParamSqr(kPrismSpeedMin, kPrismSpeedMax, 196, 255, rc);
			);
		);

		(0 <= pd)?
		(
			GoboPos(po, $0, pd, kPrismSpeedMax);
		)
		:			// rotate
		(
			GoboRotate(po, $0, ps);
		);
	);

	(po.use != pu |
	 (pu & po.whl != po.pos))?
	(
		po.use = pu;
		po.whl = po.pos;
		TEX = 1;
	);
);


gPrism[0];
gPrism2[0];

PrismTime#
(
	PrismTimed($0, gPrism,  cPsm,  cPsR);
	PrismTimed($0, gPrism2, cPsm2, cPsR);
);


// frost

kFrostMax = 0.02;		// (?)
kFrostSpeedMax = 2;		// [1/s] (of Design Spot 250)

gFrost;

FrostTime#
(
	fd = (cFrs < 128)? 0 : 1;
	(gFrost != fd)?
	(
		gFrost += LimitChange($0, fd - gFrost, kFrostSpeedMax);
		TEX = 1;
	);
);


// focus

kFocusSpeed = 1 / 1.5;	// [1/s] (of XL 700)

gFocus;

FocusTime#
(
	gFocus += LimitChange($0, cFcs / 255 - gFocus, kFocusSpeed);
);


// protocol methods


UPDATE#
(
	dir = PanTiltTime($0);

	ShutterTime($0);
	DimmerTime($0);
	CMYTime($0);
	ColorTime($0);
	GoboTime($0);
	PrismTime($0);
	FrostTime($0);
	FocusTime($0);

	PRM?
	(
		cMod = pMod;
		CTLS = 1;
		TEX = 1;

		// setup light
		l = LIGHT[0];
		l.pos[1] = kPosY;
		l.pos[2] = kZFront;
		l.dia = kDia;
		LIGHT[0] = l;
	);

	(dir | PRM)?
	(
		PAN = gPan;
		TILT = 90 + gTilt;
		SHAPE = MovingShape(kBaseWid, kBaseHi, kHeadWid, kHeadHi, kZFront, kZBack, kCenterHi, PAN, TILT, kBaseDep, kArmDep, kArmWid);
	);

	pg[0] = 1;

	// prism
	!gPrism.use?	// none
	(
		psz = 1;
	)
	:				// prism
	(
		pg[1] = 2;
		psz = kPrismSize;
	);

	!gPrism2.use?	// none
	(
	)
	:				// prism
	(
		pg[arraylength(pg)] = 3;
		psz *= kPrism2Size;
	);

	// frost
	(0 < gFrost)?
	(
		pg[arraylength(pg)] = 4;
	);

	IsProgramDiffer(PROGRAM[0], pg)?
	(
		PROGRAM[0] = pg;
		TEX = 1;
	);


	TEX?
	(
		u.pGob[0];	// objenize
		u.pGob_  = GoboMakeUniVar(gGobo.whl, 0, kGobos, pGob, u.pGob);

		u.pCol_ = ColorFilterMakeUniVar(gColor.whl, kColors, pClf);

		// effect
		u.gFrost = kFrostMax * gFrost;
		u.gPrismRot = gPrism.whl;
		u.gPrismSize = kPrismSize;
		u.gPrism2Rot = gPrism2.whl;
		u.gPrism2Size = kPrism2Size;
		u.gLastTarget = 0;

		UNIVAR[0] = u;
	);

	(
		cent = kCnt;	//ParamLin(kCnt, 2, 0, 1, gFrost);
		spread = kSpread * ParamLin(1, 1.2, 0, 1, gFrost);
		spread = PrismSpread(spread, psz);
		
		c = VecMul(kSource, gCMY);

		// setup light
		l = LIGHT[0];
		l.color = VecScale(c, gShutter * gDimmer / (psz * psz));
		l.spdh = spread;
		l.spdv = spread;
		l.cnt = cent;
		FocusSet(l, kImageRad, kDia, spread, kFocusNearest, kFocusOut, gFocus);
		LIGHT[0] = l;
	);

	PRM = CTL = 0;
);

";


src:
{

vs0:"
#define	kImageRatio		(1.0 / 2.0)		// image:space = 1:1

uniform	SGoboProp	pGob_;

varying	vec2	vPos, vTc0, vTc1;


void	main()
{
	mat3	tm0  = TexMat0(pGob_,  kImageRatio);
	mat3	tm1  = TexMat1(pGob_,  kImageRatio);

	gl_Position = ftransform();
	vPos = gl_Position.xy;

	vTc0  = (tm0  * vec3(vPos, 1.0)).xy;
	vTc1  = (tm1  * vec3(vPos, 1.0)).xy;
}
";

fs0:"
uniform	SGoboProp	pGob_;
uniform	sampler2D	pGob0, pGob1;	// (sampler array may not work well)
uniform	SColorProp	pCol_;

varying	vec2	vPos, vTc0, vTc1;


void	main()
{
	{
		vec4	c;

		c = SampleColor(pCol_, vPos.y);
		c *= SampleGobo(pGob_,  pGob0,  pGob1,  vPos, vTc0,  vTc1);

		gl_FragColor = c;
	}
}
";

Prism8C:"
#define	kFaces	(8)
#define	kDelta	(0.2)
";

Prism16C:"
#define	kFaces	(16)
#define	kDelta	(0.3)
#define	gPrismSize	gPrism2Size
#define	gPrismRot	gPrism2Rot
uniform	float	gPrismSize;
uniform	float	gPrism2Rot;	// [cycle]
";

};


prog:
{
vs:"SLgobo,SLtexmat,SLanimmat,vs0";
fs:"SLcolor,SLgobo,SLanim,SLframeFS,fs0";
},
{
vs:SLprismVS;
fs:"Prism8C,SLprismCircular,SLprismFS";
},
{
vs:SLprismVS;
fs:"Prism16C,SLprismCircular,SLprismFS";
},
{
vs:SLrectVS;
fs:SLfrostFS;
};


chmap:
"cSht cDim.w cC cM cY cCol cClh cGob cPsm cPsR cPsm2 cFrs cFcs cPan.w cTlt.w cSpd cFnc";


ctlprop:
{
	ctls:
	{
		id:cSht;
		ini:255;
	},
	{
		id:cClh;
		name:Half Color;
	};
};


prm:
{
	controls:
	{
		type:check;
		id:pRPn;
		name:"-Pan";
	},
	{
		type:check;
		id:pRTl;
		name:"-Tilt";
	},
	{
		type:array;
		id:pClfs;
		name:Color;
		label:Color;
		elemid:pClf;
		base:1;
		fixed:1;

		max:14;
		ini:14;

		controls:
		{
			type:color;
			id:pCol;
			nam:Color;
			elemini:'"(0.969, 0.086, 0.012)","(0.114, 0.557, 0.078)","(0.047, 0.176, 0.890)","(0.922, 0.953, 0.439)","(0.945, 0.118, 0.443)","(0.953, 0.486, 0.114)","(0.224, 0.953, 0.369)","(0.941, 0.325, 0.620)","(0.329, 0.584, 0.937)","(0.616, 0.933, 0.333)","(0.804, 0.529, 0.282)","(0.933, 0.839, 0.051)","(0.192, 0.945, 0.937)","(0.906, 0.871, 0.671)"';
		};
	},
	{
		type:array;
		id:pGobs;
		label:"Gobo-";
		elemid:pGob;
		base:1;
		fixed:1;

		max:15;
		ini:15;

		controls:
		{
		type:image;
		id:pGobImg;
		name:Image,画像;

		elemini:".preset/1.png,.preset/2.png,.preset/3.png,.preset/4.png,.preset/5.png,.preset/6.png,.preset/7.png,.preset/8.png,.preset/9.png,.preset/10.png,.preset/11.png,.preset/12.png,.preset/13.png,.preset/14.png,.preset/15.png";
		},
		{
		type:check;
		id:pGobNeg;
		name:Negative,陰画;
		},
		{
		type:check;
		id:pGobFlp;
		name:Flip,裏;
		},
		{
		type:numeric;
		id:pGobRot;
		name:Rotate,回転;
		min:"-180";
		max:180;
		};
	};
};

};
